package com.chess.connections.net;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chess.connections.work.WorkerThreadPool;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by wusk on 2017/12/12.
 * socket消息处理
 */
@ChannelHandler.Sharable
public class ServerMessageHandler extends SimpleChannelInboundHandler<Object> {
    private static final Logger logger = LoggerFactory.getLogger(ServerMessageHandler.class);

    private ConnectionManager clients;

    private WorkerThreadPool pool;

    private WebSocketServerHandshaker handshaker;


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object message) throws Exception {
        logger.info("Receive a message, message type: {}", message.getClass().getCanonicalName());

        if (message instanceof FullHttpRequest) {
            handleHttpRequest(ctx, ((FullHttpRequest) message));
        } else if (message instanceof TextWebSocketFrame) {
            handleTextMessage(ctx, (TextWebSocketFrame) message);
        }else if(message instanceof PongWebSocketFrame){
            ctx.channel().write(new PongWebSocketFrame(((PongWebSocketFrame) message).content().retain()));
        }else if (message instanceof CloseWebSocketFrame) {
            CloseWebSocketFrame frame = ((CloseWebSocketFrame) message).retain();
            handshaker.close(ctx.channel(), frame);
            return;
        }else{
            logger.debug("本例程仅支持文本消息，不支持二进制消息");
            throw new UnsupportedOperationException(String.format(
                    "%s frame types not supported", message.getClass().getName()));
        }
    }

    /**
     * 客户端建立连接时执行
     * @param ctx 环境信息
     * @throws Exception 异常
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info("客户端与服务端连接开启");
    }

    /**
     * 客户端断开连接时执行
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        ChannelId id = channel.id();
        clients.removeChannel(id);
        channel.close();
        logger.info("客户端与服务端连接关闭");
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    private void handleTextMessage(ChannelHandlerContext ctx, TextWebSocketFrame frame) {
        // 返回应答消息
        String message = frame.text();
        logger.info("Receive a message：{}", message);

        Channel channel = ctx.channel();

        JSONObject json = JSONObject.parseObject(message);
        String key = json.getString("key");
        if(null == key || "".equals(key)){
            Map<String, String> response = new HashMap<>(2);
            response.put("type", "message");
            response.put("message", "Wrong message type. can not get message key.");
            channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(response)));
            return;
        }
        if("register".equals(key)){
            String id = json.getString("userId");
            if(null == id || "".equals(id)){
                Map<String, String> response = new HashMap<>(2);
                response.put("type", "message");
                response.put("message", "Wrong message type. can not get register user id.");
                channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(response)));
                return;
            }
            Channel old = clients.get(id);
            if(null != old){
                Map<String, String> response = new HashMap<>(2);
                response.put("type", "message");
                response.put("message", "您已在其他客户端登录。");
                old.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(response)));
                old.close();
            }
        }
        if("query".equals(key)){
            int current = json.getIntValue("current");
            int size = json.getIntValue("size");
            int type = json.getIntValue("type");
            String id = json.getString("userId");
        }
    }

    private void handleHttpRequest(ChannelHandlerContext ctx,
                                   FullHttpRequest reqquest) {
        logger.info("Get an http request");
        if (!reqquest.decoderResult().isSuccess()
                || (!"websocket".equals(reqquest.headers().get("Upgrade")))) {

            sendHttpResponse(ctx, reqquest, new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));

            return;
        }

        logger.info("Web socket hand shake.............");
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                "ws://localhost:8030/message", null, false);

        handshaker = wsFactory.newHandshaker(reqquest);

        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            handshaker.handshake(ctx.channel(), reqquest);
        }
    }

    private static void sendHttpResponse(ChannelHandlerContext ctx,
                                         FullHttpRequest req, DefaultFullHttpResponse response) {

        // 返回应答给客户端
        if (response.status().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(response.status().toString(),
                    CharsetUtil.UTF_8);
            response.content().writeBytes(buf);
            buf.release();
        }

        // 如果是非Keep-Alive，关闭连接
        ChannelFuture f = ctx.channel().writeAndFlush(response);
        if (!isKeepAlive(req) || response.status().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    private static boolean isKeepAlive(FullHttpRequest req) {

        return true;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
